﻿using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;

namespace winS.UnityEditor.ProcessEditor
{
    /// <summary>
    /// 为<see cref="Port"/> 提供链接 <see cref="Connectionline"/> 的功能
    /// </summary>
    public class Linker
    {
        public bool isLinking { get; private set; }

        private readonly Port port;
        private readonly Connector connector;
        private readonly StepGraphView stepGraphView;

        private IEnumerable<Port> availablePorts;
        private IEnumerable<Port> unavailablePorts;
        private GhostConnectionline ghostConnectionline;

        public Linker(Port port, StepGraphView stepGraphView)
        {
            this.port = port;
            this.stepGraphView = stepGraphView;
            connector = port.connector;

            connector.RegisterCallback<MouseUpEvent>(MouseUpCallback);
            connector.RegisterCallback<MouseDownEvent>(MouseDownCallback);
            connector.RegisterCallback<MouseMoveEvent>(MouseMoveCallback);
        }

        private void MouseUpCallback(MouseUpEvent mouseUpEvent)
        {
            if (!isLinking) return;
            EndLink(mouseUpEvent.mousePosition);
        }
        private void MouseDownCallback(MouseDownEvent mouseDownEvent)
        {
            if (mouseDownEvent.button != 0) return;
            if (ghostConnectionline == null)
            {
                stepGraphView.mover.onEdgePan += offset => ghostConnectionline.UpdateByOffset(offset);
                ghostConnectionline = new GhostConnectionline(stepGraphView);
                ghostConnectionline.style.color = stepGraphView.processEditor.editorData.connectionLineSetting.normalColor * new Color(1f, 1f, 1f, 0.5f);
            }
            StartLink();
            mouseDownEvent.StopImmediatePropagation();
        }
        private void MouseMoveCallback(MouseMoveEvent mouseMoveEvent)
        {
            if (!isLinking) return;
            stepGraphView.mover.CalculatePanSpeed(mouseMoveEvent.mousePosition);//鼠标此时被connector捕获, mover 无法接收MouseMoveEvent, 手动计算
            ghostConnectionline.Update(stepGraphView.connectionlineContainer.WorldToLocal(mouseMoveEvent.mousePosition));
        }

        public void StartLink()
        {
            isLinking = true;
            stepGraphView.mover.EnableEdgePan();

            connector.ShowCap();
            connector.CaptureMouse();
            availablePorts = stepGraphView.GetAvailablePorts(port);
            unavailablePorts = stepGraphView.GetAllPorts().Except(availablePorts).ToList();
            foreach (var port in unavailablePorts) port.Disable();
            stepGraphView.AddConnectionline(ghostConnectionline);
            ghostConnectionline.Start(stepGraphView.connectionlineContainer.WorldToLocal(port.GetConnectorWorldPoint()));
        }
        public void EndLink(Vector2 mousePosition)
        {
            isLinking = false;
            stepGraphView.mover.DisableEdgePan();
            connector.ReleaseMouse();
            stepGraphView.RemoveConnectionline(ghostConnectionline);
            foreach (var port in unavailablePorts) port.Enable();

            //检查是否连接
            foreach (var availablePort in availablePorts)
            {
                if (!availablePort.worldBound.Contains(mousePosition)) continue;
                if (availablePort.IsConnectTo(port)) return;
                if (port.portType == PortType.Input) stepGraphView.ConnectPort(port as InputPort, availablePort as OutputPort);
                else stepGraphView.ConnectPort(availablePort as InputPort, port as OutputPort);
                return;
            }
            //未连接
            if (!port.isConnected) connector.HideCap();
            if (port is OutputPort output)
            {
                stepGraphView.waitConnectionOutputPort = output;
                stepGraphView.OpenSearchWindow(mousePosition);
            }
        }
    }
}